package com.googlecode.curvedmetromaps.core;

import static playn.core.PlayN.*;

import com.googlecode.curvedmetromaps.core.logic.forcedriven.ForceSystem;
import com.googlecode.curvedmetromaps.core.model.MetroMap;
import com.googlecode.curvedmetromaps.core.presentation.Renderer;

import playn.core.Canvas;
import playn.core.CanvasImage;
import playn.core.Game;
import playn.core.ImageLayer;
import playn.core.Mouse;
import playn.core.Mouse.WheelEvent;
import playn.core.PlayN;
import playn.core.SurfaceImage;
import playn.core.util.Callback;

public class MetroMapr extends Game.Default {

	private CanvasImage bgCanvasImage;
	private CanvasImage mapCanvasImage;
	private SurfaceImage mapSurface;
	private ImageLayer mapLayer;
	private Renderer renderer;

	// Camera
	private float camZoom = 1;
	private float camX = 0;
	private float camY = 0;

	// Main line
	private MetroMap metroLine;
	private boolean isMapLoaded = false;

	// Force system
	private ForceSystem forceSystem;

	public MetroMapr() {
		// Set time in ms between update calls
		super(1);
	}

	@Override
	public void init() {
		int w = graphics().width();
		int h = graphics().height();

		mapSurface = graphics().createSurface(w, h);

		// Create layer that contains the surface
		mapLayer = graphics().createImageLayer();
		mapLayer.setImage(mapSurface);
		graphics().rootLayer().add(mapLayer);

		// create canvas to render map
		bgCanvasImage = graphics().createImage(w, h);
		mapCanvasImage = graphics().createImage(w, h);

		// create metro renderer
		renderer = new Renderer();

		// Load the metro map
		//createMetroMap("maps/osm/Madrid.xml");
		createMetroMap("maps/json/len3.json");

		// Mouse events
		setMouseListeners();
	}

	@Override
	public void update(int delta) {
		if (isMapLoaded == false)
			return;

		// Create ForceSystem if it does not exist
		if (forceSystem == null)
			forceSystem = new ForceSystem(metroLine);

		forceSystem.update(delta);
	}

	@Override
	public void paint(float alpha) {
		int w = graphics().width();
		int h = graphics().height();

		Canvas bgCanvas = bgCanvasImage.canvas();
		bgCanvas.setFillColor(0xFFFFFFFF);
		bgCanvas.fillRect(0, 0, w, h);

		Canvas canvas = mapCanvasImage.canvas();
		canvas.clear();
		if (isMapLoaded) {
			renderer.drawMetroMap(canvas, metroLine);
		}

		mapSurface.surface().drawImage(bgCanvasImage, 0, 0);
		mapSurface.surface().drawImage(mapCanvasImage, 0, 0);
	}

	private void createMetroMap(String map) {
		// Load metro map
		MapLoader.load(map, new Callback<MetroMap>() {
			@Override
			public void onSuccess(MetroMap resource) {
				metroLine = resource;
				isMapLoaded = true;
			}

			@Override
			public void onFailure(Throwable err) {
				PlayN.log().error(
						"Error loading metro map: " + err.getMessage());
			}
		});
	}

	private void setMouseListeners() {
		// add a listener for pointer (mouse, touch) input
		mouse().setListener(new Mouse.Adapter() {
			@Override
			public void onMouseWheelScroll(WheelEvent event) {
				// Zoom
				float z = (float) Math.pow(1.1f, -event.velocity());
				// Translation
				float x = ((event.localX() - camX) * (1 - z));
				float y = ((event.localY() - camY) * (1 - z));

				// Perform translate, scale on canvas.
				Canvas canvas = mapCanvasImage.canvas();
				canvas.translate(x / camZoom, y / camZoom);
				canvas.scale(z, z);

				camZoom *= z;
				camX += x;
				camY += y;
			}
		});
	}
}
